﻿using System;
using System.Runtime.InteropServices;
using Heaven.Mathematics;
using Heaven.Graphics;

using XNA = Microsoft.Xna.Framework.Graphics;
using XnaFramework = Microsoft.Xna.Framework;

namespace Heaven.Xna.Graphics
{
    /// <summary>
    /// Конвертация различных типов и перечислений
    /// </summary>
    internal class Converter
    {
        /// <summary>
        /// Конвертация данных из массива 
        /// произвольных структур в массив байтов
        /// </summary>
        /// <typeparam name="T">Структура</typeparam>
        /// <param name="data">Массив структур</param>
        /// <param name="start">Индекс в исходном массиве с которого начинать</param>
        /// <param name="count">Колическтво элементов для копирования</param>
        /// <returns>Массив байтов</returns>
        public static byte[] ToArray<T>(T[] data, int start, int count) where T : struct
        {            
            int size = Marshal.SizeOf(data[0]);
            byte[] result = new byte[size * count];
            for(int i = start; i < start + count; i++)
            {
                for(int j = 0; j < size; j++)
                {
                    result[(i - start) * size + j] = Marshal.ReadByte(data[i], j); 
                }
            }
            return result;
        }

        /*/// <summary>
        /// Копирование из масиива структур в массив байтов
        /// </summary>
        /// <typeparam name="T">Структура</typeparam>
        /// <param name="data">Массив структур</param>
        /// <param name="buffer">Массив байтов</param>
        /// <param name="start">Индекс в исходном массиве с которого начинать</param>
        /// <param name="count">Колическтво элементов для копирования</param>
        public static void Copy<T>(T[] data, byte[] buffer, int start, int count) where T : struct
        {            
            int size = Marshal.SizeOf(data[0]);
            
            for(int i = start; i < start + count; i++)
            {
                for(int j = 0; j < size; j++)
                {
                    buffer[i * size + j] = Marshal.ReadByte(data[i], j); 
                }
            }            
        }*/

        /// <summary>
        /// Конвертация Heaven.Mathematics.Matrix4 в Microsoft.Xna.Framework.Matrix 
        /// </summary>
        /// <param name="m">Матрица Heaven.Mathematics.Matrix4 для конвертации</param>
        /// <returns>Microsoft.Xna.Framework.Matrix матрица</returns>
        static public XnaFramework.Matrix Convert(Matrix4 m)
        {
            return new Microsoft.Xna.Framework.Matrix(
                (float)m.M11, (float)m.M12, (float)m.M13, (float)m.M14,
                (float)m.M21, (float)m.M22, (float)m.M23, (float)m.M24,
                (float)m.M31, (float)m.M32, (float)m.M33, (float)m.M34,
                (float)m.M41, (float)m.M42, (float)m.M43, (float)m.M44);
        }

        /// <summary>
        /// Конвертация Microsoft.Xna.Framework.Matrix в Heaven.Mathematics.Matrix4 
        /// </summary>
        /// <param name="m">Матрица Microsoft.Xna.Framework.Matrix для конвертации</param>
        /// <returns>Heaven.Mathematics.Matrix4 матрица</returns>
        static public Matrix4 Convert(Microsoft.Xna.Framework.Matrix m)
        {
            return new Matrix4( m.M11, m.M12, m.M13, m.M14,
                                m.M21, m.M22, m.M23, m.M24,
                                m.M31, m.M32, m.M33, m.M34,
                                m.M41, m.M42, m.M43, m.M44);
        }

        /*/// <summary>
        /// Конвертация формата пикселя
        /// </summary>
        /// <param name="format">Формат пикселя в здесь</param>
        /// <returns>Формат пикселя в XNA</returns>
        static public XNA.SurfaceFormat Convert(SurfaceFormat format)
        {
            switch (format)
            {
                case SurfaceFormat.Rgba32: return XNA.SurfaceFormat.Color;
                case SurfaceFormat.Float: return XNA.SurfaceFormat.Single;
                case SurfaceFormat.Half4: return XNA.SurfaceFormat.HalfVector4;
                default:
                    throw new Exception("Формат пока не определен: " + format);
            }
        }*/

       /* /// <summary>
        /// Конвертация формата пикселя
        /// </summary>
        /// <param name="format">Формат пикселя в XNA</param>
        /// <returns>Формат пикселя в здесь</returns>
        static public SurfaceFormat Convert(XNA.SurfaceFormat format)
        {
            switch (format)
            {
                case XNA.SurfaceFormat.Color: return SurfaceFormat.Rgba32;
                case XNA.SurfaceFormat.Single: return SurfaceFormat.Float;
                case XNA.SurfaceFormat.HalfVector4: return SurfaceFormat.Half4;
                default:
                    throw new Exception("Формат пока не определен: " + format);
            }
        }*/

        /// <summary>
        /// Конвертация режима отсечения полигонов
        /// </summary>
        /// <param name="cullMode">Режим отсечения в локальном формате</param>
        /// <returns>Режим отсечения в формате XNA</returns>
        static public XNA.CullMode Convert(CullMode cullMode)
        {
            switch (cullMode)
            {
                case CullMode.Clockwise:
                    return XNA.CullMode.CullClockwiseFace;
                case CullMode.CounterClockwise:
                    return XNA.CullMode.CullCounterClockwiseFace;
                case CullMode.None:
                    return XNA.CullMode.None;
                default:
                    throw new Exception("Режим отсечения пока не определен: " + cullMode);
            }
        }

        /// <summary>
        /// Конвертация режима отсечения полигонов
        /// </summary>
        /// <param name="cullMode">Режим отсечения в формате XNA</param>
        /// <returns>Режим отсечения в локальном формате</returns>
        static public CullMode Convert(XNA.CullMode cullMode)
        {
            switch (cullMode)
            {
                case XNA.CullMode.CullClockwiseFace:
                    return CullMode.Clockwise;
                case XNA.CullMode.CullCounterClockwiseFace:
                    return CullMode.CounterClockwise;
                case XNA.CullMode.None:
                    return CullMode.None;
                default:
                    throw new Exception("Режим отсечения пока не определен: " + cullMode);
            }
        }
     
        /// <summary>
        /// Конвертация типов данных представляющих собой вершину
        /// </summary>
        /// <param name="type">Тип здешний</param>
        /// <returns>Тип в XNA</returns>
        static public XNA.VertexElementFormat Convert(DeclarationType type)
        {
            return (XNA.VertexElementFormat)type;
        }

        /// <summary>
        /// Конвертация методов декларации вершин
        /// </summary>
        /// <param name="method">Метод декларации здесь</param>
        /// <returns>Метод декларации в XNA</returns>
        static public XNA.VertexElementMethod Convert(DeclarationMethod method)
        {
            switch (method)
            {
                case DeclarationMethod.Default:
                    return XNA.VertexElementMethod.Default;
                case DeclarationMethod.LookUp:
                    return XNA.VertexElementMethod.LookUp;
                case DeclarationMethod.LookUpPresampled:
                    return XNA.VertexElementMethod.LookUpPresampled;
                case DeclarationMethod.UV:
                    return XNA.VertexElementMethod.UV;
                default:
                    throw new ArgumentException("Пока не определено: " + method);
            }
        }

        /// <summary>
        /// Конвертация режимов предназначения вершинных данных
        /// </summary>
        /// <param name="usage">Предназначение вершинных данных здесь</param>
        /// <returns>Предназначение вершинных данных в XNA</returns>
        static public XNA.VertexElementUsage Convert(DeclarationUsage usage)
        {
            return (XNA.VertexElementUsage)usage;
        }

        /// <summary>
        /// Конвертация режимов предназначения вершинных данных
        /// </summary>
        /// <param name="usage">Предназначение вершинных данных в XNA</param>
        /// <returns>Предназначение вершинных данных здесь</returns>
        static public DeclarationUsage Convert(XNA.VertexElementUsage usage)
        {
            return ((DeclarationUsage)usage);
        }
        
        /// <summary>
        /// Конвертация четырёхмерного вектора
        /// </summary>
        /// <param name="vec">Четырехмерный вектор здесь</param>
        /// <returns>Четырехмерный вектор Xna</returns>
        static public Microsoft.Xna.Framework.Vector4 Convert(Vector4 vec)
        {
            return new Microsoft.Xna.Framework.Vector4((float)vec.X, (float)vec.Y, (float)vec.Z, (float)vec.W);
        }

        /// <summary>
        /// Конвертация четырёхмерного вектора
        /// </summary>
        /// <param name="vec">Четырехмерный вектор Xna</param>
        /// <returns>Четырехмерный вектор здесь</returns>
        static public Vector4 Convert(Microsoft.Xna.Framework.Vector4 vec)
        {
            return new Vector4(vec.X, vec.Y, vec.Z, vec.W);
        }

        /// <summary>
        /// Конвертация трёхмерного вектора
        /// </summary>
        /// <param name="vec">Трехмерный вектор здесь</param>
        /// <returns>Трехмерный вектор Xna</returns>
        static public Microsoft.Xna.Framework.Vector3 Convert(Vector3 vec)
        {
            return new Microsoft.Xna.Framework.Vector3((float)vec.X, (float)vec.Y, (float)vec.Z);
        }

        /// <summary>
        /// Конвертация трёхмерного вектора
        /// </summary>
        /// <param name="vec">Трехмерный вектор XNA</param>
        /// <returns>Трехмерный вектор здесь</returns>
        static public Vector3 Convert(Microsoft.Xna.Framework.Vector3 vec)
        {
            return new Vector3(vec.X, vec.Y, vec.Z);
        }

        /// <summary>
        /// Конвертация цветов
        /// </summary>
        /// <param name="color">Цвет в XNA</param>
        /// <returns>Цвет здешний</returns>
        static public Color Convert(XNA.Color color)
        {
            Microsoft.Xna.Framework.Vector4 vector = color.ToVector4();
            return new Color(vector.X, vector.Y, vector.Z, vector.W);
        }

        /// <summary>
        /// Конвертация цветов
        /// </summary>
        /// <param name="color">Цвет в System.Drawing</param>
        /// <returns>Цвет в XNA</returns>
        static public XnaFramework.Vector4 ConvertToVector4(Color color)
        {
            return Convert(color).ToVector4();
        }

        /// <summary>
        /// Конвертация цветов
        /// </summary>
        /// <param name="color">Цвет в System.Drawing</param>
        /// <returns>Цвет в XNA</returns>
        static public XNA.Color Convert(Color color)
        {
            return new XNA.Color(new Microsoft.Xna.Framework.Vector4(
                (float)color.Red, (float)color.Green, (float)color.Blue, (float)color.Alpha));
        }
    }
}
